import eruda from "eruda"
import jsQR from "jsqr"
import axios from "axios"

import getVisitorId from "./utils/fingerprint.js"
import {generateGaussianKernel, applyGaussianBlur, calculateSharpness, calculateBrightness, calculateSkewAngles} from "./utils/algorithm.js"
import {useDialog, useToast, useTip, useDrawer} from "./utils/dialog.js"
import {getQuery, formatQuery} from "./utils/index.js"

/** 全局变量 */
//地址参数
const query = getQuery()

let visitorId = null

// 调试
const debug = query.debug === "true" ? true : false // 是否开启调试模式，默认关闭
const local_debug = query.local_debug === "true" ? true : false // 是否开启本地调试模式，默认关闭
const crop = query.crop === "true" ? true : false
const brand = query.brand || navigator.userAgent
const time = Boolean(!!query.time)

// 2K分辨率(2048×1080) 3K分辨率(3036×2024) 4K分辨率(4096×2160)
const pixel = {TWO_K: 2048, THREE_K: 3036, FOUR_K: 4096}
const isMobileDevice = /Android|HarmonyOS|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
const isOpenHarmonyDevice = /OpenHarmony/i.test(navigator.userAgent)
const isAndroidDevice = /Android/i.test(navigator.userAgent)
const isIosDevice = /iPhone/i.test(navigator.userAgent)

// apis
const apis = {
  190: "http://192.168.31.190:8000",
  187: "http://192.168.31.187:8000",
  147: "http://192.168.99.69:8000",
  9090: "http://192.168.31.187:9090",
  local: "http://172.20.10.2:8000",
  production: "https://www.antifakefire.com:8040",
  productionIos: "https://www.antifakefire.com:8040"
}
const common_api = query.api ? apis[query.api] : isIosDevice ? apis["productionIos"] : apis["production"]
const api = query.api ? apis[query.api] : isIosDevice ? apis["productionIos"] : apis["production"]
const configure = {iosThreshold: {}, darkThreshold: {}, lightThreshold: {}}

/** 初始化 */
if (debug || local_debug) {
  //  eruda 调试工具
  eruda.init()
} else {
  // 日志模式关闭
  console.log = function () {}
}

const Dialog = useDialog()
const Toast = useToast()
const Tip = useTip()
const Drawer = useDrawer()

// 设置根字体大小（通常在页面初始化时执行）
const docEl = document.documentElement
let rem = 0
const resize = () => {
  const clientWidth = docEl.clientWidth || 375
  rem = clientWidth / 750
  docEl.style.fontSize = rem + "px" // 750rrem = 100%宽度
}
window.addEventListener("resize", resize)
resize()

// userAgent
console.log("userAgent", navigator.userAgent)
//设置机型
if (isAndroidDevice) {
  console.log("安卓设备")
  docEl.setAttribute("data-device", "android")
}
if (isOpenHarmonyDevice) {
  console.log("鸿蒙设备")
  docEl.setAttribute("data-device", "harmony")
}
if (isIosDevice) {
  console.log("IOS设备")
  docEl.setAttribute("data-device", "ios")
}

/** 请求方法 */
function fetchConfigure() {
  return new Promise((resolve, reject) => {
    axios({
      url: common_api + "/api/watermark/threshold",
      method: "GET"
    })
      .then(res => {
        resolve(res.data)
      })
      .catch(err => {
        reject(err)
      })
  })
}

function UploadCodeImage(blob, type, value, size) {
  return new Promise((resolve, reject) => {
    let data = new FormData()
    data.append("image", blob)
    data.append("body", JSON.stringify({goodsClass: markType, deviceId: visitorId, brandName: brand}))
    data.append("type", type)
    data.append("value", value || "")
    data.append("size", size || "")
    axios({
      url: api + (!time ? "/api/watermark/android_qr/up_load/take_picture" : "/api/watermark/android_qr/up_load"),
      method: "POST",
      data: data
    })
      .then(res => {
        resolve(res.data)
      })
      .catch(err => {
        reject(err)
      })
  })
}

function RegisterCodeImage(blob, info) {
  return new Promise((resolve, reject) => {
    let data = new FormData()
    data.append("image", blob)
    data.append("body", info)
    axios({
      url: common_api + "/api/watermark/register",
      method: "POST",
      data: data
    })
      .then(res => resolve(res.data))
      .catch(err => {
        reject(err)
      })
  })
}

function UploadDeviceInfo(devices) {
  const isSaved = localStorage.getItem("saveDevice")

  if (isSaved) return

  const devicesInfo = {
    deviceId: visitorId,
    brand: navigator.userAgent.split(")")[0] + ")",
    model: "",
    cameras: devices.map(el => {
      const params = (el.getCapabilities && el.getCapabilities()) || {}
      return {
        cameraName: el.label,
        resolution: `${(params.width && JSON.stringify(params.width)) || ""} * ${(params.height && JSON.stringify(params.height)) || ""}`,
        frameRate: (params.frameRate && JSON.stringify(params.frameRate)) || ""
      }
    })
  }

  let data = new FormData()
  data.append("body", JSON.stringify(devicesInfo))
  axios({
    url: common_api + "/api/device/save",
    method: "POST",
    data: data
  }).then(() => {
    localStorage.setItem("saveDevice", true)
  })
}

/** 类函数 */
class CompatibilityError extends Error {
  constructor(e) {
    super(e), (this.name = "CompatibilityError"), Object.setPrototypeOf(this, CompatibilityError.prototype)
  }
}
class PlatformError extends Error {
  constructor() {
    super("不支持32位版本的微信\n请重新安装64位版本的微信"), (this.name = "PlatformError")
  }
}
class DecoderError extends Error {
  constructor(e) {
    super(e), (this.name = "DecoderError")
  }
}

class FrameRateController {
  constructor(func, fps) {
    // 配置参数
    this.callback = func
    this.fps = fps || 30

    // 时间控制系统
    this._lastTime = null
    this._interval = 1000 / this.fps // 目标帧间隔(ms)

    // 绑定上下文防止丢失
    this.frame = this.frame.bind(this)

    this._isPaused = false
  }

  // 启动动画循环
  start() {
    this._isPaused = false
    this.frame()
  }
  // 停止动画循环
  stop() {
    this._isPaused = true
    this._lastTime = null
  }

  // 核心动画循环
  frame() {
    if (this._isPaused) return
    const currentTime = performance.now()
    // 计算时间差
    const delta = currentTime - (this._lastTime || currentTime)
    //帧执行
    if (delta >= this._interval || delta === 0) {
      this._lastTime = currentTime
      // 渲染
      this.callback && this.callback()
    }
    // 请求下一帧
    requestAnimationFrame(this.frame)
  }
}

class cWorker {
  constructor(workUrl) {
    this.worker = new Worker(workUrl)
    this.status = "init"
    this.workMap = {}
    this.eventMap = {}

    this.worker.onmessage = event => {
      console.log("main 收到消息:", event.data)
      const {id, type, data, error} = event.data

      if (id && this.workMap[id]) {
        if (error) {
          this.workMap[id].reject(error)
        } else {
          this.workMap[id].resolve(data)
        }
        delete this.workMap[id]
      } else if (this.eventMap[type]) {
        if (type === "ready") this.status = "ready"
        this.eventMap[type](event.data)
      }
    }
  }
  getId() {
    return Date.now() + Math.random().toString(36).substring(2)
  }
  on(type, callback) {
    this.eventMap[type] = callback
  }
  postMessage(data) {
    return new Promise((resolve, reject) => {
      const id = this.getId()
      this.workMap[id] = {resolve, reject}
      this.worker.postMessage({id, ...data}, data)
    })
  }
}

class MediaDevice {
  constructor() {
    // 设备
    this.devices = []
  }
  async initDevice() {
    try {
      if (/WeChat\/arm32/i.test(navigator.userAgent)) throw new PlatformError()

      if (navigator.mediaDevices === void 0 && (navigator.mediaDevices = {}) && navigator.mediaDevices.enumerateDevices === void 0) throw new CompatibilityError("当前浏览器不支持enumerateDevices API")

      navigator.mediaDevices.getUserMedia === void 0 &&
        (navigator.mediaDevices.getUserMedia = function (e) {
          const sameName = navigator.webkitGetUserMedia || navigator.mozGetUserMedia
          return sameName
            ? new Promise((resolve, reject) => {
                sameName.call(navigator, e, resolve, reject)
              })
            : Promise.reject(new CompatibilityError("当前浏览器不支持getUserMedia API"))
        })

      //初始化摄像权限
      const firstStream = await navigator.mediaDevices.getUserMedia({video: {facingMode: "environment"}})
      firstStream.getVideoTracks().forEach(el => el.stop())
      // 枚举设备
      const devices = await navigator.mediaDevices.enumerateDevices()

      // 过滤设备
      this.devices = devices.filter(el => el.kind === "videoinput")

      console.log(
        "devices: ",
        this.devices.map(el => el.label)
      )
    } catch (error) {
      "NotAllowedError" === error.name
        ? new DecoderError("摄像机使用权限被拒绝")
        : error instanceof OverconstrainedError
        ? new CompatibilityError(`当前设备无法满足摄像头使用条件（${error.constraint}）`)
        : new CompatibilityError(error.message)

      console.log("initDevice err: ", error)
    }
  }
}

class MediaVideo {
  constructor(video) {
    this.video = video
    this.device = null
    this.stream = null
    this.deviceParams = null
    /** 高斯模糊 */
    this.kernel = generateGaussianKernel(5, 1.2)
    /** 裁剪区域 */
    this.canvasSize = null
    /** 裁剪X轴 */
    this.canvasXAxis = null
    /** 裁剪Y轴 */
    this.canvasYAxis = null
    this.canvas = document.getElementById("canvas")
    this.correctCanvas = document.getElementById("correctCanvas")
    this.qrCanvas = document.getElementById("qrCanvas")
  }
  // 启动摄像头
  async startCamera(option = {}) {
    try {
      const confireuse = {
        video: {
          ...option
        }
      }
      // 缓存 设备参数
      this.deviceParams = option
      // 启动
      const stream = await navigator.mediaDevices.getUserMedia(confireuse)
      this.stream = stream
      return stream
    } catch (error) {
      console.error("摄像头切换失败:", error, error.message, error.constraint, error.name)
      return null
    }
  }
  // 停止摄像头
  stopCamera() {
    this.stream && this.stream.getTracks().forEach(track => track.stop())
    this.video && this.video.srcObject instanceof MediaStream && this.video.srcObject.getTracks().forEach(e => e.stop())
    this.stream = null
  }
  // 重载摄像头
  async reloadCamera() {
    //没有视频流时就重载
    if (!this.stream) {
      await this.startCamera(this.deviceParams)
      await this.selectDevice(this.device)
    }
  }
  // 初始画布
  initCanvas(video) {
    const sizeWidth = (isIosDevice ? 300 : 400) * rem

    // 屏幕坐标
    const {clientWidth, clientHeight} = document.documentElement
    // video坐标
    const {top, left, width, height} = this.video.getBoundingClientRect()
    const {videoWidth, videoHeight} = this.video

    // 缩放率
    const scale = videoWidth / width

    const start_x = (clientWidth - sizeWidth) / 2
    const start_y = (clientHeight - sizeWidth) / 2

    // 画布大小
    this.canvasSize = sizeWidth * scale
    // 裁剪坐标
    this.canvasXAxis = (start_x - left) * scale
    this.canvasYAxis = (start_y - top) * scale

    canvas.width = this.canvasSize
    canvas.height = this.canvasSize

    correctCanvas.width = this.canvasSize
    correctCanvas.height = this.canvasSize

    console.log(`video缩放：${Math.floor(scale)}, 截取大小：${Math.floor(this.canvasSize)}`)
    console.log(`屏幕宽高 ${Math.floor(clientWidth)}*${Math.floor(clientHeight)}`)
    console.log(`video实际宽高 ${Math.floor(width)}*${Math.floor(height)} video流宽高 ${Math.floor(videoWidth)}*${Math.floor(videoHeight)} `)
    console.log(`video位置 ${Math.floor(top)}*${Math.floor(left)} 取景框XY点 ${Math.floor(start_x)}*${Math.floor(start_y)} videoXY点 ${Math.floor(this.canvasXAxis)}*${Math.floor(this.canvasYAxis)}`)
  }
  /** 初始化video位置 */
  initVideoPosition() {
    // 获取页面可视区域的宽度和高度
    const {clientWidth, clientHeight} = document.documentElement
    const {videoWidth, videoHeight} = this.video

    // 计算视频显示区域大小：竖屏时取宽度的2/3，横屏时取高度的2/3
    const ratio = clientHeight > clientWidth ? (2 * clientWidth) / 3 : (2 * clientHeight) / 3

    // 计算缩放比例
    const _e = ratio / 480

    // 安卓设备特殊处理
    if (isAndroidDevice || isOpenHarmonyDevice) {
      // const _t = clientHeight * (2 - (clientWidth / clientHeight))
      // this.video.width = _t
      this.video.height = clientHeight
    } else if (isIosDevice) {
      this.video.height = clientHeight
    } else {
      // 其他设备使用CSS transform进行缩放
      this.video.style.transform = `translate(-50%, -50%)scale(${_e})`
    }
  }
  /** 选择 */
  async selectDevice(device, pixel = 2048) {
    if (!device) return

    //自动清理
    if (this.stream) {
      this.stopCamera()
    }

    this.device = device

    const {clientWidth, clientHeight} = document.documentElement

    const deviceParams = device.getCapabilities && device.getCapabilities()

    const aspectRatio = clientWidth / clientHeight
    const verticalRatio = clientHeight > clientWidth

    console.log("选择设备ID：", device.deviceId)
    console.log("选择设备名称：", device.label)
    console.log("选择设备参数：", `${JSON.stringify(deviceParams?.width || {})} * ${JSON.stringify(deviceParams?.height || {})}`)

    const cameraParams = {
      facingMode: "environment",
      deviceId: device.deviceId,
      width: 0,
      height: 0,
      frameRate: {
        min: 30,
        max: 60
      }
    }
    // 计算宽高比例
    let widthRatio, heightRatio
    if (deviceParams) {
      const deviceWidthRatio = deviceParams?.width?.max || 1080
      const deviceHeightRatio = deviceParams?.height?.max || 1920

      if (crop) {
        if (isIosDevice) {
          widthRatio = deviceWidthRatio // 宽度
          heightRatio = deviceHeightRatio // 高度
        } else {
          if (verticalRatio) {
            widthRatio = Math.round(deviceHeightRatio * aspectRatio) // 宽度
            heightRatio = deviceHeightRatio // 高度
          } else {
            widthRatio = deviceWidthRatio // 宽度
            heightRatio = Math.round(deviceWidthRatio / aspectRatio) // 高度
          }
        }
      } else {
        widthRatio = deviceWidthRatio // 宽度
        heightRatio = deviceHeightRatio // 高度
      }
    } else {
      if (verticalRatio) {
        widthRatio = Math.round(pixel * aspectRatio) // 宽度
        heightRatio = pixel // 高度
      } else {
        widthRatio = pixel // 宽度
        heightRatio = Math.round(pixel / aspectRatio) // 高度
      }
    }

    cameraParams.width = widthRatio
    cameraParams.height = heightRatio

    // 安卓设备 切换方向
    if (isAndroidDevice || isOpenHarmonyDevice) {
      const cloneWidth = cameraParams.width
      cameraParams.width = cameraParams.height
      cameraParams.height = cloneWidth
    }
    // 打开摄像头
    const stream = await this.startCamera(cameraParams)

    console.log("video参数 : ", cameraParams)
    console.log("video元素检测 : ", Boolean(this.video))
    console.log("video视频流检测 : ", Boolean(stream))

    // 渲染视频流
    if ("srcObject" in this.video)
      try {
        this.video.srcObject = stream
      } catch (t) {
        if ("TypeError" !== t.name) throw t
        this.video.src = URL.createObjectURL(stream)
      }
    else this.video.src = URL.createObjectURL(stream)
    this.video?.play()
    this.video.style.display = "block"
  }

  // 拍照
  takeCamera() {
    return new Promise((resolve, reject) => {
      if (this.stream && this.video && this.video.tagName !== "VIDEO") {
        return reject("拍照失败，画布不存在！")
      }

      const ctx = this.canvas.getContext("2d")

      //清理画布
      ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)

      // 取图
      ctx.drawImage(this.video, this.canvasXAxis, this.canvasYAxis, this.canvasSize, this.canvasSize, 0, 0, this.canvas.width, this.canvas.height)

      // 图片待矫正
      const imageData = ctx.getImageData(0, 0, this.canvas.width, this.canvas.height) || {}

      //高斯模糊
      const blurredImageData = applyGaussianBlur(imageData, this.kernel)

      const qrCodeInfo = jsQR(blurredImageData.data, blurredImageData.width, blurredImageData.height)

      if (!qrCodeInfo) {
        if (local_debug) {
          console.log("二维码不存在！")
          return this.canvas.toBlob(blob => {
            resolve({blob, type: 1, value: ``, size: 0})
          })
        } else {
          return reject("二维码不存在！")
        }
      }

      // 二次重置画布
      const correctCtx = this.correctCanvas.getContext("2d")
      const qrCtx = this.qrCanvas.getContext("2d")

      correctCtx.clearRect(0, 0, this.correctCanvas.width, this.correctCanvas.height)

      let {correctedTopLeft, qrSize, rotationRadian, verticalSkew, horizontalSkew} = calculateSkewAngles(qrCodeInfo.location, imageData.width, imageData.height)
      console.log(
        `二维码 旋转坐标:${correctedTopLeft.x.toFixed(2)}*${correctedTopLeft.y.toFixed(2)} 大小:${qrSize.toFixed(2)} 旋转角度:${rotationRadian.toFixed(5)} 水平倾斜:${verticalSkew.toFixed(
          3
        )} 垂直倾斜:${horizontalSkew.toFixed(3)}`
      )

      // 角度检测
      if (verticalSkew >= 10 || horizontalSkew >= 10) {
        Tip.show("请保持设备平衡！", {time: 1500})
        if (local_debug) {
          console.log("图片过于倾斜！")
          return this.canvas.toBlob(blob => {
            resolve({
              blob,
              type: 2,
              value: `${verticalSkew.toFixed(3)}*${horizontalSkew.toFixed(3)}`,
              size: Math.floor(qrSize)
            })
          })
        } else {
          return reject(`图片过于倾斜! ${Math.floor(verticalSkew)}*${Math.floor(horizontalSkew)}`)
        }
      }

      // 矫正码图
      correctCtx.save()
      correctCtx.translate(this.correctCanvas.width / 2, this.correctCanvas.height / 2)
      correctCtx.rotate(-rotationRadian)
      correctCtx.drawImage(
        this.canvas,
        0,
        0,
        this.correctCanvas.width,
        this.correctCanvas.height,
        -this.correctCanvas.width / 2,
        -this.correctCanvas.height / 2,
        this.correctCanvas.width,
        this.correctCanvas.height
      )
      correctCtx.restore()

      // 添加间距
      qrSize += 40
      correctedTopLeft.x = correctedTopLeft.x - 20 > 0 ? correctedTopLeft.x - 20 : 0
      correctedTopLeft.y = correctedTopLeft.y - 20 > 0 ? correctedTopLeft.y - 20 : 0

      // 获取裁剪二维码
      const qrImageData = correctCtx.getImageData(correctedTopLeft.x, correctedTopLeft.y, qrSize, qrSize)

      // 亮度检测
      let light = ""
      const brightness = calculateBrightness(qrImageData.data)
      console.log("亮度：", brightness)

      if (brightness < configure.darkThreshold.brightnessDown) {
        Tip.show("注意避免光线过暗！", {time: 1500})
        if (local_debug) {
          console.log("图片光线过暗！")
          return this.correctCanvas.toBlob(blob => {
            resolve({blob, type: 3, value: `${Math.floor(brightness)}`, size: Math.floor(qrSize)})
          })
        } else {
          return reject(`图片光线过暗！ ${Math.floor(brightness)}`)
        }
      }
      if (brightness > configure.lightThreshold.brightnessUp) {
        Tip.show("注意避免光线过曝！", {time: 1500})
        if (local_debug) {
          console.log("图片光线过曝！")
          return this.correctCanvas.toBlob(blob => {
            resolve({blob, type: 3, value: `${Math.floor(brightness)}`, size: Math.floor(qrSize)})
          })
        } else {
          return reject(`图片光线过曝！ ${Math.floor(brightness)}`)
        }
      }
      if (configure.darkThreshold.brightnessDown < brightness < configure.darkThreshold.brightnessUp) {
        light = "dark"
      }
      if (configure.lightThreshold.brightnessDown < brightness < configure.lightThreshold.brightnessUp) {
        light = "light"
      }

      // 清晰度检测
      const sharpness = calculateSharpness(qrImageData)
      console.log("清晰度：", sharpness)

      if (light === "dark") {
        if (sharpness < configure.darkThreshold.definition) {
          Tip.show("保持设备稳定！", {time: 1500})
          if (local_debug) {
            console.log("图片清晰度不足！")
            return this.correctCanvas.toBlob(blob => {
              resolve({blob, type: 4, value: Math.floor(sharpness), size: Math.floor(qrSize)})
            })
          } else {
            return reject(`图片清晰度不足！ ${Math.floor(sharpness)}`)
          }
        }
      } else {
        if (sharpness < configure.lightThreshold.definition) {
          Tip.show("保持设备稳定！", {time: 1500})
          if (local_debug) {
            console.log("图片清晰度不足！")
            return this.correctCanvas.toBlob(blob => {
              resolve({blob, type: 4, value: Math.floor(sharpness), size: Math.floor(qrSize)})
            })
          } else {
            return reject(`图片清晰度不足！ ${Math.floor(sharpness)}`)
          }
        }
      }

      // 贴码
      this.qrCanvas.width = qrSize
      this.qrCanvas.height = qrSize
      //清理画布
      qrCtx.clearRect(0, 0, this.qrCanvas.width, this.qrCanvas.height)
      qrCtx.putImageData(qrImageData, 0, 0)

      //返回
      qrCanvas.toBlob(blob => {
        console.log("二维码大小：" + Math.ceil(blob.size / 1024) + "KB")
        resolve({blob, type: 5, value: `${Math.floor(sharpness)}`, size: Math.floor(qrSize)})
      })
    })
  }
}

/** main */
let markType = "Origin"
try {
  const video = document.getElementById("video")

  const deviceClass = new MediaDevice("environment")
  console.log("deviceClass", deviceClass)

  const videoClass = new MediaVideo(video)
  console.log("videoClass", videoClass)

  //帧扫码 30帧
  const frameClass = new FrameRateController(triggerFn, 25)

  // 扫描状态
  let scanTrigger = true
  let scanResult = false
  let scanLocateErrCount = 0
  let scanPolarizationErrCount = 0

  const markTypeEnum = ["Origin", "Without", "Copy", "A4"]

  async function mainFn() {
    try {
      let defaultPixel = isAndroidDevice || isOpenHarmonyDevice ? pixel.TWO_K : pixel.THREE_K

      const {clientWidth, clientHeight} = document.documentElement

      console.log("屏幕宽高 :", clientWidth, clientHeight)

      if (deviceClass.devices.length === 0) {
        alert("该设备不存在识别硬件！")
      }

      console.log("设备型号匹配1：")
      //渲染设备列表
      const devicesContent = []
      for (const _device of deviceClass.devices) {
        devicesContent.push(`<div class="camera-item" data-deviceId="${_device.deviceId}">${_device.label}</div>`)
      }
      document.querySelector(".camera-list").innerHTML = devicesContent.join("")
      console.log("设备型号匹配2：")
      // 选中主摄像头
      let rootCamera = null

      const cameraRules = new Map([
        //  HUAWEINOH-AL00/HUAWEINOH-AL10/Mete 40 Pro
        //  HUAWEIMIA-AL00/Nova 14 Pro
        //  HUAWEIMRT-AL10/Nova 14 Ultra
        //  HUAWEIBRA-AL00/Mete 60
        //  HUAWEIPLR-AL00/HUAWEIPLR-AL30/Mete 70Pro
        //  HUAWEIPLA-AL10/Mete 70Pro+
        //  HUAWEIHBN-AL80/HUAWEIHBN-AL10/pure70pro+
        //  HUAWEIHED-AL00/pure80
        //  HUAWEILMR-AL00/HUAWEIHBN-AL10/pure80pro 暂不处理
        //  HUAWEILMR-AL10/HUAWEIHBN-AL10/pure80pro+ 暂不处理
        [
          [
            "HUAWEINOH-AL00",
            "HUAWEINOH-AL10",
            "HUAWEIMIA-AL00",
            "HUAWEIMRT-AL10",
            "HUAWEIBRA-AL00",
            "HUAWEIPLR-AL00",
            "HUAWEIPLR-AL30",
            "HUAWEIPLA-AL10",
            "HUAWEIHBN-AL80",
            "HUAWEIHBN-AL10",
            "HUAWEILMR-AL00",
            "HUAWEIHBN-AL10",
            "HUAWEIHED-AL00"
          ],
          /(camera2 2, facing back)|(device\/2)/
        ],
        //  HUAWEIICL-AL10/Mete x6
        //  HUAWEIVDE-AL00/pure x
        [["HUAWEIICL-AL10", "HUAWEIVDE-AL00"], /(camera2 3, facing back)/],
        ["iPhone", /(后置超广角摄像头)/]
      ])
      console.log("设备型号匹配3：")
      // 使用映射进行查找
      let huawei_key = [...cameraRules.keys()].find(key => (Array.isArray(key) ? key.some(k => brand.indexOf(k) !== -1) : brand.indexOf(key) !== -1))

      console.log("设备型号匹配4：", huawei_key)

      if (/(HUAWEI)/.test(brand) && huawei_key) {
        // 处理华为系列
        const huawei_reg = cameraRules.get(huawei_key)

        console.log("huawei_reg", huawei_reg)

        rootCamera = deviceClass.devices.find(el => huawei_reg.test(el.label))
      } else if (/(iPhone).+(Pro)/.test(brand)) {
        // 处理苹果pro系列（使用正则匹配）
        rootCamera = deviceClass.devices.find(el => cameraRules["iPhone"].test(el.label))
      } else {
        // 默认情况
        rootCamera = deviceClass.devices.find(el => /(微距|主摄像头|后置相机|camera2 0|device\/0)/.test(el.label))
      }

      let rootPixel = defaultPixel

      if (!rootCamera) {
        if (isAndroidDevice || isOpenHarmonyDevice) {
          rootCamera = deviceClass.devices[deviceClass.devices.length - 1]
        } else {
          const deviceMax = deviceClass.devices.map(el => {
            const device_params = {
              device: el,
              max: 0
            }
            const _params = el.getCapabilities()
            if (_params) {
              const width = _params.width ? _params.width.max : 0
              const height = _params.height ? _params.height.max : 0
              device_params.max = Math.max(width, height)
            }
            return device_params
          })

          const deviceMaxPixel = Math.max(...deviceMax.map(el => el.max))

          if (deviceMaxPixel >= pixel.FOUR_K) {
            rootPixel = pixel.FOUR_K
          } else if (deviceMaxPixel >= pixel.THREE_K) {
            rootPixel = pixel.THREE_K
          }
          rootCamera = deviceMax.find(el => el.max == deviceMaxPixel).device
        }
      }

      console.log("默认摄像头：", rootCamera)
      if (rootCamera) {
        await videoClass.selectDevice(rootCamera, rootPixel)
        //选择设备
        ;(document.querySelectorAll(".camera-list .camera-item") || []).forEach(el => {
          if (rootCamera.deviceId == el.dataset.deviceid) {
            el.classList.add("active")
          } else {
            el.classList.remove("active")
          }
        })
      } else {
        alert("调用摄像头失败！")
      }

      video.addEventListener("play", () => {
        console.log("video 视频流播放")
      })
      video.addEventListener("loadedmetadata", () => {
        const {videoWidth, videoHeight} = video
        console.log(`video 视频流分辨率：${videoWidth}x${videoHeight}`)
        // 更新位置
        videoClass.initVideoPosition(video)
        // 绘制画布
        videoClass.initCanvas(video)
        // 开始扫码
        frameClass.start()
        // 提示
        Tip.show("请对准防伪标识码！", {time: 10000})
      })
      video.addEventListener("pause", () => {
        console.log("video 暂停")
      })
    } catch (error) {
      console.log("camera error:", error)
    }
  }

  function goResultPage(type, data) {
    const params = formatQuery({type, ...(data || {})})

    if (!time) {
      // 测试忽略结果界面
      scanResult = false
      scanTrigger = true
      return
    }

    switch (query.source) {
      case "check":
        //检查
        wx.miniProgram.navigateTo({
          url: "/pages/result/index" + params
        })
        break
      case "register":
        //注册
        wx.miniProgram.navigateTo({
          url: "/pages/result/index" + params
        })
        break
      default:
        scanTrigger = true
    }
  }

  async function goCheck() {
    try {
      const {blob, type, value, size} = await videoClass.takeCamera()

      if (scanResult) return

      const data = await UploadCodeImage(blob, type, value, size)

      switch (data.code) {
        case 1000: //识别成功
          scanResult = true
          Toast.show("识别成功！")
          goResultPage("success", data.goodsBrand)
          scanLocateErrCount = 0
          scanPolarizationErrCount = 0
          break
        case 1001: //继续识别
          scanLocateErrCount++
          scanPolarizationErrCount = 0

          if (scanLocateErrCount == 3) {
            scanResult = true
            scanLocateErrCount = 0
            Toast.show("识别错误！")
            goResultPage("error")
          } else {
            Tip.show("请对准防伪标识码！", {time: 1000})
          }
          break
        case 1002: //识别失败
          scanLocateErrCount = 0
          scanPolarizationErrCount++
          if (scanPolarizationErrCount == 2) {
            scanResult = true
            Toast.show("识别错误！")
            goResultPage("error")
            scanPolarizationErrCount = 0
          } else {
            Tip.show("请对准防伪标识码！", {time: 1000})
          }
          break
        case 1010: //采集次数
          alert("该类别已采集到一万张！")
          scanLocateErrCount = 0
          scanPolarizationErrCount = 0
          break
        default:
          scanLocateErrCount = 0
          scanPolarizationErrCount = 0
      }
    } catch (err) {
      console.log("takeCamera err", err.message)
    } finally {
      // 恢复扫描
      scanTrigger = true
    }
  }

  async function goRegister() {
    try {
      const {blob, type} = await videoClass.takeCamera()

      if (scanResult) return

      const data = RegisterCodeImage(
        blob,
        JSON.stringify({
          brandTitle: query.brandTitle,
          productTitle: query.productTitle
        })
      )

      switch (data.code) {
        case 1001: //继续识别
          scanLocateErrCount++
          scanPolarizationErrCount = 0
          if (scanLocateErrCount == 3) {
            scanResult = true
            Toast.show("识别错误！")
            goResultPage("error")
            scanLocateErrCount = 0
          } else {
            Tip.show("请对准防伪标识码！")
          }
          break
        case 1002: //识别失败
          scanLocateErrCount = 0
          scanPolarizationErrCount++
          if (scanPolarizationErrCount == 2) {
            scanResult = true
            Toast.show("识别错误！")
            goResultPage("error")
            scanPolarizationErrCount = 0
          } else {
            Tip.show("请对准防伪标识码！")
          }
          break
        case 1003: //已注册
          scanLocateErrCount = 0
          scanPolarizationErrCount = 0
          Toast.show("该标签已注册！")
          break
        case 1004: //注册成功！
          scanResult = true
          Toast.show("注册成功！")
          goResultPage("register", data.goodsBrand)
          scanLocateErrCount = 0
          scanPolarizationErrCount = 0
          break
        case 1010: //采集次数
          alert("该类别已采集到一万张！")
          scanLocateErrCount = 0
          scanPolarizationErrCount = 0
          break
        default:
          scanLocateErrCount = 0
          scanPolarizationErrCount = 0
      }
    } catch (err) {
      console.log("takeCamera err", err.message)
    } finally {
      // 恢复扫描
      scanTrigger = true
    }
  }

  async function triggerFn() {
    // 没有扫描成功 继续触发
    if (!scanResult && scanTrigger) {
      //停止拍照
      scanTrigger = false

      switch (query.source) {
        case "check":
          //检查
          await goCheck()
          break
        case "register":
          //检查
          await goRegister()
          break
        default:
          await goCheck()
      }
    }
  }

  //预数据
  const preload = Promise.all([getVisitorId(), fetchConfigure(), deviceClass.initDevice()])

  //打开抽屉
  document.body.querySelector(".camera-toggle").addEventListener("click", () => {
    Drawer.open({
      selector: ".drawer",
      direction: "left",
      duration: 300,
      maskClosable: true
    })
  })
  //切换摄像头
  document.body.querySelector(".camera-list").addEventListener("click", async e => {
    const target = e.target

    const device = deviceClass.devices.find(el => el.deviceId === target.dataset.deviceid)
    //选择设备
    ;(document.querySelectorAll(".camera-list .camera-item") || []).forEach(el => {
      console.log("deviceId", el)

      if (device.deviceId == el.dataset.deviceid) {
        el.classList.add("active")
      } else {
        el.classList.remove("active")
      }
    })

    await videoClass.selectDevice(device)
    //关闭抽屉
    Drawer.close()
  })

  document.addEventListener("visibilitychange", async function () {
    console.log("页面可见状态：", document.visibilityState)
    if (document.visibilityState === "visible") {
      // 页面可见时的操作
      scanResult = false
      scanTrigger = true
      // 重载摄像头
      await videoClass.reloadCamera()
      // 播放
      video.play && video.play()
      video.style.display = "block"
      // 开始扫码
      frameClass.start()
    } else {
      // 页面隐藏时的操作
      scanResult = true
      scanTrigger = false
      video.style.display = "none"
      // 暂停扫码
      frameClass.stop()
      // 停止视频流
      if (isIosDevice) {
        videoClass.stopCamera()
      }
    }
    //更新状态
    Tip.show("请对准防伪标识码！")
    scanLocateErrCount = 0
    scanPolarizationErrCount = 0
  })

  window.onload = async function () {
    //是否开启摄像头切换
    if (!time) {
      document.querySelector(".camera-toggle").style.display = "block"
    }

    function startupApp() {
      preload.then(result => {
        const [clientId, config] = result

        console.log("brand", brand)
        console.log("设备ID：", clientId)
        console.log("全局参数阈值：", config)
        //同步visitorId
        visitorId = clientId
        //上传设备信息
        UploadDeviceInfo(deviceClass.devices)
        //同步配置
        Object.assign(configure, config)
        //启动
        mainFn()
      })
    }

    //根据环境启动是否配置参数
    if (!time) {
      Dialog.create({
        mask: true,
        styles: {
          body: {}
        },
        content: '<div class="select-list">' + markTypeEnum.map(el => `<div class="select-li" data-event="${el}">${el}</div>`).join("") + "</div>",
        event: function (evtName, e) {
          markType = evtName

          startupApp()
          Dialog.close()
        }
      })
    } else {
      startupApp()
    }
  }
} catch (err) {
  console.log("err", err)
}
